/************************************************************************
* builtin.c
* Copyright (C) Lisa Milne 2013 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "dish.h"
#include "env.h"

static int bi_exit(int argc, char** argv, char** env, int in, int out, int err)
{
	int e = 0;
	if (argc > 1)
		e = strtol(argv[1],NULL,10);
	exit_state(e);
	write(out,"\n",1);
	return -2;
}

static int bi_envdump(int argc, char** argv, char** env, int in, int out, int err)
{
	int i;
	for (i=0; env[i]; i++) {
		write(out,env[i],strlen(env[i]));
		write(out,"\n",1);
	}
	return 0;
}

static int bi_cd(int argc, char** argv, char** env, int in, int out, int err)
{
	char* d;
	char buff[1024];

	if (argc > 1) {
		d = argv[1];
	}else{
		d = env_get("HOME");
	}

	if (!is_dir(d)) {
		if (!is_something(d)) {
			write(err,"error: cd: '",12);
			write(err,d,strlen(d));
			write(err,"' No such file or directory\n",28);
		}else{
			write(err,"error: cd: '",12);
			write(err,d,strlen(d));
			write(err,"' Is not a directory\n",21);
		}
		return 1;
	}

	chdir(d);
	getcwd(buff,1024);
	env_set("PWD",buff,1);

	return 0;
}

static int bi_export(int argc, char** argv, char** env, int in, int out, int err)
{
	char buff[1024];
	int p = 0;
	char** e;
	char* b;
	int i;
	int o;
	for (i=1; i<argc; i++) {
		if (!strcmp(argv[i],"-p")) {
			p = 1;
			continue;
		}
		env_set(argv[i],NULL,1);
	}
	if (!p)
		return 0;

	e = env_toclist();

	for (i=0; e[i]; i++) {
		b = strchr(e[i],'=');
		*b = 0;
		b++;
		if (!*b) {
			p = sprintf(buff,"export %s\n",e[i]);
			write(out,buff,p);
			continue;
		}
		o = sprintf(buff,"export %s=",e[i]);
		if (strchr(b,' ') || strchr(b,'"') || strchr(b,'\'')) {
			buff[o++] = '"';
			while (*b) {
				if (*b == '"')
					buff[o++] = '\\';
				buff[o++] = *b;
			}
			buff[o++] = '"';
		}else{
			strcat(buff,b);
		}
		strcat(buff,"\n");
		write(out,buff,strlen(buff));
	}

	clist_free(e);

	return 0;
}

/************************************************************************
* The stuff that makes the above functions work
************************************************************************/
typedef struct builtin_s {
	char* name;
	unsigned int h_name;
	int (*func)(int,char**,char**,int,int,int);
} builtin_t;

static int builtin_isinit = 0;
static int builtin_checked = -1;
static builtin_t builtins[] = {
	{"cd",0,bi_cd},
	{"envdump",0,bi_envdump},
	{"exit",0,bi_exit},
	{"exec",0,NULL},
	{"export",0,bi_export},
	{NULL,0,NULL}
};
static void builtin_init()
{
	int i;
	for (i=0; builtins[i].name; i++) {
		builtins[i].h_name = hash(builtins[i].name);
	}
}

/* check if a command is a builtin */
int builtin_check(char* fn)
{
	unsigned int h = hash(fn);
	if (!builtin_isinit)
		builtin_init();
	for (builtin_checked=0; builtins[builtin_checked].name; builtin_checked++) {
		if (builtins[builtin_checked].h_name == h && !strcmp(builtins[builtin_checked].name,fn))
			return 1;
	}
	return 0;
}

/* execute a builtin command */
int builtin_exec(int argc, char** argv, char** env, int in, int out, int err)
{
	if (!builtins[builtin_checked].func)
		return -1;
	return builtins[builtin_checked].func(argc,argv,env,in,out,err);
}
